package top.chmin.project.cloud.common;

import cn.hutool.json.JSONUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import top.chmin.project.cloud.tool.LocalTool;
import top.chmin.project.cloud.tool.SpringTool;

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Stream;

/**
 * chmin
 * 2024/1/11 9:34
 * cloud-code
 */
@Getter
@Setter
@Slf4j
public class Condition {
    protected String sql = "";
    protected List<Object> valueList = new ArrayList<>();

    protected boolean distinct = false;
    protected Class<? extends Model> modelClass;
    protected List<String> selectList = new ArrayList<>();

    protected List<String> updateList = new ArrayList<>();
    protected List<Object> updateValueList = new ArrayList<>();

    protected List<String> whereList = new ArrayList<>();
    protected List<Object> whereValueList = new ArrayList<>();

    protected List<String> groupList = new ArrayList<>();

    protected List<String> orderList = new ArrayList<>();

    protected String limit = "";

    private void complete() {
        if (sql.contains("#select#")) {
            sql = sql.replace("#select#", (distinct ? "DISTINCT " : "") + String.join(",", selectList));
        }
        if (sql.contains("#update#")) {
            sql = sql.replace("#update#", " SET " + String.join(",", updateList));
            valueList.addAll(updateValueList);
        }
        if (sql.contains("#where#")) {
            sql = sql.replace("#where#", whereList.isEmpty() ? "" : " WHERE " + String.join(" AND ", whereList));
            valueList.addAll(whereValueList);
        }
        if (sql.contains("#group#")) {
            sql = sql.replace("#group#", groupList.isEmpty() ? "" : " GROUP BY " + String.join(",", groupList));
        }
        if (sql.contains("#order#")) {
            sql = sql.replace("#order#", orderList.isEmpty() ? "" : " ORDER BY " + String.join(",", orderList));
        }
        if (sql.contains("#limit#")) {
            sql = sql.replace("#limit#", limit);
        }
        log.info("sql: {}", sql);
        log.info("valueList: {}", JSONUtil.toJsonStr(valueList));
    }

    public int execute() {
        complete();
        JdbcTemplate template = SpringTool.getBean(JdbcTemplate.class);
        return template.update(sql, valueList.toArray());
    }

    public <T> T selectFieldFirst(Class<T> clazz) {
        limit = " LIMIT 1";
        return selectFieldList(clazz).getFirst();
    }

    public <T> T selectFirst(Class<T> clazz) {
        new SelectHandler(this).autoSelect(clazz);
        limit = " LIMIT 1";
        try {
            return selectList(clazz).getFirst();
        } catch (NoSuchElementException e) {
            return null;
        }
    }

    public <T> List<T> selectFieldList(Class<T> clazz) {
        complete();
        JdbcTemplate template = SpringTool.getBean(JdbcTemplate.class);
        return template.queryForList(sql, clazz, valueList.toArray());
    }

    public <T> List<T> selectList(Class<T> clazz) {
        return selectStream(clazz).toList();
    }

    public <T> Stream<T> selectStream(Class<T> clazz) {
        new SelectHandler(this).autoSelect(clazz);
        complete();
        JdbcTemplate template = SpringTool.getBean(JdbcTemplate.class);
        return template.queryForStream(sql, new BeanPropertyRowMapper<>(clazz), valueList.toArray());
    }

    public <T> Page<T> selectPage(Class<T> clazz) {
        Condition condition = new Condition();
        condition.setDistinct(distinct);
        condition.setModelClass(modelClass);
        condition.setSelectList(List.of("count(id)"));
        condition.setUpdateList(updateList);
        condition.setUpdateValueList(updateValueList);
        condition.setWhereList(whereList);
        condition.setWhereValueList(whereValueList);
        condition.setGroupList(groupList);
        condition.setOrderList(orderList);
        condition.setSql(sql);
        Integer total = condition.selectFieldFirst(Integer.class);
        if (total == 0) {
            return new Page<>();
        }
        setLimit(" LIMIT %s,%s".formatted(
                (Math.min(LocalTool.getPage().getPage() - 1, (total + LocalTool.getPage().getPageSize() - 1) / LocalTool.getPage().getPageSize())) * LocalTool.getPage().getPageSize(),
                LocalTool.getPage().getPageSize()
        ));
        return new Page<T>(LocalTool.getPage()).setTotal(total).setList(selectList(clazz));
    }
}
